package net.cyndeline.GUI.graphics

import com.escalatesoft.subcut.inject.BindingModule
import net.cyndeline.GUI.MapModifier
import com.escalatesoft.subcut.inject.Injectable
import javafx.scene.canvas.Canvas
import net.cyndeline.GUI.canvas.Painter
import net.cyndeline.GUI.canvas.CanvasPainter
import javafx.scene.layout.Pane
import net.cyndeline.entitySystem.EntityRef
import javafx.scene.image.Image
import net.cyndeline.GUI.Background
import javafx.scene.layout.StackPane
import javafx.geometry.Pos
import net.cyndeline.GUI.Agent

class GraphicsModifier(implicit val bindingModule: BindingModule) extends MapModifier with Injectable {
  private val painter = injectOptional[Painter] getOrElse { new CanvasPainter() }
  private val buffer = injectOptional[GFXBuffer] getOrElse new GFXBuffer()
  
  private var pane = new StackPane()
  private var canvas = new Canvas()
  pane.getChildren().add(canvas)
  
  private var tileSize = (1, 1)
  
  /* Content layers. Only one image may be present on each layer. */
  private var agents = Array.ofDim[Image](1, 1)
  private var background = Array.ofDim[Image](1, 1)
  
  override def map = {
    pane
  }
  
  override def clear() {
    painter.clear()
  }
  
  override def create(width: Int, height: Int, tileXAmount: Int, tileYAmount: Int) {
    canvas = new Canvas(width, height)
    pane = new StackPane()
    pane.getChildren().add(canvas)
    pane.setAlignment(Pos.CENTER)
    
    background = Array.ofDim[Image](tileXAmount, tileYAmount)
    agents = Array.ofDim[Image](tileXAmount, tileYAmount)
    tileSize = (tileXAmount, tileYAmount)
    painter.setCanvas(canvas)
  }
  
  override def addObjects(x: Int, y: Int, entities: List[EntityRef]) {
    val converter = new EntityKeyConverter()
    val converted = converter.formatForRendering(entities)
    
    /* Clear current data */
    background(x)(y) = null
    agents(x)(y) = null
    
    /* Get all background items, display the first one. */
    val bgs = converted.filter(key_layer => key_layer._2 == Background)
    
    if (!bgs.isEmpty) {
      background(x)(y) = buffer.get(bgs(0)._1)
    }
    
    /* Get all agents */
    val agentsList = converted.filter(key_layer => key_layer._2 == Agent)
    if (!agentsList.isEmpty) {
      agents(x)(y) = buffer.get(agentsList(0)._1)
    }
    
  }
  
  override def moveObject(mover: EntityRef, start: (Int, Int), stop: (Int, Int), newTile: List[EntityRef], oldTile: List[EntityRef]) {
    addObjects(start._1, start._2, oldTile)
    addObjects(stop._1, stop._2, newTile)
  }
  
  override def addFogOfWar(x: Int, y: Int) {
    
  }
  
  override def removeFogOfWar(x: Int, y: Int) {
    
  }
  
  override def mouseHighlightOn(x: Int, y: Int) {
    
  }
  
  override def mouseHighlightOff(x: Int, y: Int) {
    
  }
  
  override def walkPathHighlightOn(x: Int, y: Int) {
    
  }
  
  override def walkPathHighlightOff(x: Int, y: Int) {
    
  }
  
  override def paint() {
    val cWidth = canvas.getWidth()
    val cHeight = canvas.getHeight()
    val drawWidth = Math.round(cWidth / tileSize._1).toInt
    val drawHeight = Math.round(cHeight / tileSize._2).toInt
    
    //painter.drawRectangle(canvas.getWidth(), canvas.getHeight(), 0, 0)
    clear()
    
    /* All images will be drawn so that the bottom side aligns with the
     * bottom of the tile, rather than basing it on the upper left corner.
     * 
     * To do this, the upper left corner must be positioned with the lower
     * side in mind.
     * 
     * Y: Upper left corner - (Image height - tile height)
     * 
     * x: Upper left corner - ((Image width / 2) - (tile width / 2))
     */
    
    for (m <- 0 until background.length; n <- 0 until background(0).length) {
      val bg = background(m)(n)
      if (bg != null) {
        painter.drawImage(bg, upperLeftX(bg, drawWidth, m), upperLeftY(bg, drawHeight, n))
      }
      
      val ag = agents(m)(n)
      if (ag != null) {
        painter.drawImage(ag, m * drawWidth, n * drawHeight)
      }
    }
  }
  
  private def upperLeftX(img: Image, tileWidth: Int, drawIndex: Int): Int = {
    val corner = drawIndex * tileWidth
    (corner - ((img.getWidth / 2) - (tileWidth / 2))).toInt
  }
  
  private def upperLeftY(img: Image, tileHeight: Int, drawIndex: Int): Int = {
    val corner = drawIndex * tileHeight
    (corner - (img.getHeight - tileHeight)).toInt
  }
}